--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 099994333d150292659fc328be293556334b9f22
Parents : 4ae020a
Author : Ivan <ivan@quad4.io>
Signature : Invalid signer <e46112d44649266d71fe2193e00a4710>, author is <ivan@quad4.io>
Date : 2026-06-19T10:24:01-05:00
feat(page_node): improve file handling by ensuring bytearray support and registering existing files during announcements and listings
Changes
2 files changed, 24 insertions(+), 2 deletions(-)
Diff
diff --git a/meshchatx/src/backend/page_node.py b/meshchatx/src/backend/page_node.py
index 2e6ff773..fd367020 100644
--- a/meshchatx/src/backend/page_node.py
+++ b/meshchatx/src/backend/page_node.py
@@ -125,6 +125,7 @@ class PageNode:
def announce(self):
"""Broadcast this node's presence on the mesh."""
if self.destination and self.running:
+ self._register_existing_files()
app_data = self.name.encode("utf-8")
self.destination.announce(app_data=app_data)
self._ensure_local_path()
@@ -360,8 +361,9 @@ class PageNode:
name = _safe_mesh_file_basename(name)
_reject_name_component_too_long(self.files_dir, name)
file_path = os.path.join(self.files_dir, name)
- mode = "wb" if isinstance(data, bytes) else "w"
- with open(file_path, mode) as f:
+ if isinstance(data, str):
+ data = data.encode("utf-8")
+ with open(file_path, "wb") as f:
f.write(data)
if self.running:
self._register_file_handler(name)
@@ -383,6 +385,8 @@ class PageNode:
def list_files(self):
"""Return a list of file dicts with name and size."""
+ if self.running:
+ self._register_existing_files()
if not os.path.isdir(self.files_dir):
return []
result = []
diff --git a/tests/backend/test_page_node.py b/tests/backend/test_page_node.py
index 213f4a6d..7ee60be9 100644
--- a/tests/backend/test_page_node.py
+++ b/tests/backend/test_page_node.py
@@ -260,6 +260,15 @@ class TestPageNodeFiles:
with open(path, "rb") as f:
assert f.read() == b"\x00\x01\x02"
+ def test_add_file_writes_bytearray(self, node_dir, mock_rns):
+ node = _make_node(node_dir, mock_rns)
+ node.setup()
+ name = node.add_file("upload.bin", bytearray(b"\xde\xad\xbe\xef"))
+ assert name == "upload.bin"
+ path = os.path.join(node.files_dir, "upload.bin")
+ with open(path, "rb") as f:
+ assert f.read() == b"\xde\xad\xbe\xef"
+
def test_add_file_registers_handler(self, node_dir, mock_rns):
node = _make_node(node_dir, mock_rns)
node.setup()
@@ -463,6 +472,15 @@ class TestPageNodeEdgeCases:
node.setup()
assert "/file/pre.txt" in node._registered_file_paths
+ def test_list_files_registers_manually_added_files(self, node_dir, mock_rns):
+ node = _make_node(node_dir, mock_rns)
+ node.setup()
+ with open(os.path.join(node.files_dir, "dropped.bin"), "wb") as f:
+ f.write(b"dropped")
+ assert "/file/dropped.bin" not in node._registered_file_paths
+ node.list_files()
+ assert "/file/dropped.bin" in node._registered_file_paths
+
def test_path_traversal_blocked(self, node_dir, mock_rns):
node = _make_node(node_dir, mock_rns)
node.setup()
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────